package ldh.text.controller.segment;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import ldh.text.util.RegexUtil;
import ldh.text.util.ThreadPoolUtil;
import org.fxmisc.richtext.StyleClassedTextArea;
import org.fxmisc.richtext.model.TwoDimensional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FindPaneController {

    private static final Logger LOGGER = LoggerFactory.getLogger(FindPaneController.class);
    private StyleClassedTextArea textArea;
    private int findMaxSize;
    private TextStatusPaneController textStatusPaneController;

    // 查询
    @FXML private Pane searchPane;
    @FXML private TextField searchTextField;
    @FXML private Pane findResultPane;
    @FXML private Label findResultLabel;
    @FXML private Label tipLabel;

    private int currentSelect = 0;
    private List<int[]> searchRangeList = new LinkedList();
    private int[] currentPosition;

    public void setTextArea(StyleClassedTextArea textArea, int findMaxSize) {
        this.textArea = textArea;
        this.findMaxSize = findMaxSize;
        initEvent();
    }

    public void setTextStatusPaneController(TextStatusPaneController textStatusPaneController) {
        this.textStatusPaneController = textStatusPaneController;
    }

    private void initEvent() {
        textArea.setOnKeyPressed(e->{
            if (e.isControlDown() && e.getCode() == KeyCode.F) {// 查找
                searchPane.setVisible(true);
                searchPane.setManaged(true);
            }
        });

        textArea.caretPositionProperty().addListener(l->{
            int position = textArea.getCaretPosition();
            textStatusPaneController.setCartPosition(textArea.getCurrentParagraph(), textArea.getCaretColumn());
            if (position < 1) return;
//            ThreadPoolUtil.submit(()->{
//                for(int i=0, lg=searchRangeList.size()-1; i<lg; i++) {
//                    if (searchRangeList.get(i)[0] < position && searchRangeList.get(i+1)[0] > position) {
//                        currentSelect = i;
//                        break;
//                    }
//                }
//            });
        });
    }

    @FXML
    public void closeSearchPane(ActionEvent actionEvent) {
        searchPane.setVisible(false);
        searchPane.setManaged(false);
    }

    @FXML
    public void search(ActionEvent actionEvent) {
        String searchText = searchTextField.getText().trim();
        if (searchText.equals("")) return;
        findText(searchText);
    }

    @FXML
    public void moveToNext(ActionEvent actionEvent) {
        if (!isSelectInShow()) {
            currentSelect = calcCaretLine();
        }
        if (searchRangeList.size() < 1) return;
        currentSelect++;
        if (searchRangeList.size() <= currentSelect) {
            currentSelect = 0;
        }
        moveTo(searchRangeList.get(currentSelect)[0], searchRangeList.get(currentSelect)[1]);
    }

    @FXML
    public void moveToPre(ActionEvent actionEvent) {
        if (!isSelectInShow()) {
            currentSelect = calcCaretLine();
        }
        if (searchRangeList.size() < 1) return;
        currentSelect--;
        if (currentSelect < 0) {
            currentSelect = searchRangeList.size() - 1;
        }
        moveTo(searchRangeList.get(currentSelect)[0], searchRangeList.get(currentSelect)[1]);

    }

    private void findText(String searchText) {
        if (textArea == null) return;
        int lg = searchRangeList.size();
        lg = lg > findMaxSize ? findMaxSize : lg;
        for(int i=0; i<lg; i++) {
            int[] array = searchRangeList.get(i);
            textArea.clearStyle(array[0], array[1]);
        }
        searchRangeList.clear();
        currentSelect = 0;
        Platform.runLater(()-> {
            Pattern pattern = Pattern.compile(searchText);
            Matcher matcher = pattern.matcher(textArea.getText());
            int t = matcher.groupCount();
            LOGGER.info("search total:" + t);
            while(matcher.find()) {
                int start = matcher.start();
                int end = matcher.end();
                searchRangeList.add(new int[]{start, end});
            }
            final int length = searchRangeList.size();
            LOGGER.info("search total:" + length);
            if (length < 1) return;
            Platform.runLater(()->{
                findResultPane.setManaged(true);
                findResultLabel.setText("共查找到" + length + "结果");
                int showSize = length;
                if (length > findMaxSize) {
                    showSize = findMaxSize;
                    tipLabel.setManaged(true);
                    tipLabel.setText("查询内容太多，只标注前" + showSize + "个，请优化查询关键词");
                }
                for (int i=0; i<showSize; i++) {
                    int[] array = searchRangeList.get(i);
                    textArea.setStyleClass(array[0], array[1], "select-text");
                }
                LOGGER.info("set style end!");
            });

        });
    }

    private void moveTo3(int start, int end) {
        ThreadPoolUtil.submit(()->{
            String content = textArea.getText();
            String p = content.substring(0, start);
            int plength = RegexUtil.totalFind(p, "\r|\n");
            Platform.runLater(()->{
                try {
                    if (textArea.firstVisibleParToAllParIndex() <= plength && textArea.lastVisibleParToAllParIndex() > plength) {
                        textArea.showParagraphAtTop(plength);
                    }
                    if (currentPosition == null) {
                        textArea.selectRange(start, end);
                    } else {
                        if (start > currentPosition[0]) {
                            textArea.offsetToPosition(start - currentPosition[0], TwoDimensional.Bias.Forward);
                        } else {
                            textArea.offsetToPosition(currentPosition[0] - start, TwoDimensional.Bias.Backward);
                        }
                    }
                    currentPosition = new int[]{start, end};
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        });
    }

    private void moveTo(int start, int end) {
        textArea.moveTo(start);
        textArea.selectRange(start, end);
        textArea.requestFollowCaret();

        double x=0, y=0;
        if (textArea.getSelectionBounds().isPresent()) {
            x = textArea.getSelectionBounds().get().getMinX();
            y = textArea.getSelectionBounds().get().getMaxY();
        } else if (textArea.getCaretBounds().isPresent()) {
            x = textArea.getCaretBounds().get().getMinX();
            y = textArea.getCaretBounds().get().getMinY();
        }

        if (x>0 || y > 0) {
            Point2D point2D = textArea.localToScene(0, 0);
            double screenX = x + point2D.getX();
            double screenY = y +point2D.getY();
            textArea.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED, 1, 1, screenX, screenY, MouseButton.PRIMARY, 1,
                    true, true, true, true, true, true, true, true, true, true, null));

        }
    }

    private int calcCaretLine() {
        int position = textArea.getAbsolutePosition(textArea.firstVisibleParToAllParIndex(), 0);
//        for(int i=0, lg=searchRangeList.size()-1; i<lg; i++) {
//            if (searchRangeList.get(i)[0] < position && searchRangeList.get(i+1)[0] > position) {
//                return i;
//            }
//        }
        int t = binarySearch(searchRangeList, position);
        return t == 0 ? position : t;
    }

    private int binarySearch(List<int[]> list, int position) {
        int l = 0, r = list.size() - 1;
        while(l <= r) {
            int mid = (l + r + 1) / 2;
            if (list.get(mid)[0] < position && mid + 1 < list.size() && list.get(mid+1)[0] > position) {
                return mid;
            } else if (list.get(mid)[0] > position) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return 0;
    }

    private boolean isSelectInShow() {
        int start = textArea.firstVisibleParToAllParIndex();
        int last = textArea.lastVisibleParToAllParIndex();
        int currentParagraph = textArea.getCurrentParagraph();
        if (currentParagraph >= start && currentParagraph <= last) {
            return true;
        }
        return false;
    }
}
